/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "Precacher.h"
#include <resource/Manager.h>
#include <Gluck.h>
#include <xml/Stream.h>
#include <lib3d/node/Factory.h>
#include <animate/Soul.h>

#include <boost/scoped_ptr.hpp>

namespace liba
{
namespace lib3d
{

Precacher::Precacher()
:	precache_root( Atom("ApplicationGameConstructor::precache_root") ),
	on_a_level(false),
	precaching(true)
{}

Precacher::~Precacher()
{
	on_a_level = false;
	for(size_t i = 0; i < models_on_level.size(); ++i)
	{
		delete nodemap[models_on_level[i]]; nodemap[models_on_level[i]] = 0;
	}
	models_on_level.clear();
}

void Precacher::precache(const filesystem::String & filename)
{
	precaching = true;

	animate::Soul::Creator::disable_registration();
	
	boost::scoped_ptr<resource::Resource> res(resource::Manager::create_resource( filename ));
	if( !res.get() )
		THROW(GluckBadResource(__FUNCTION__,"No precache file", filename ));

	if( !xml::StreamResource( res.get() ).load( &xml::Root(this, "Scene") ) )
		THROW(GluckBadResource(__FUNCTION__,"Cannot load precache file", filename ));
	
	animate::Soul::Creator::enable_registration();
}

void Precacher::uncache(const filesystem::String & filename)
{
	precaching = false;
	boost::scoped_ptr<resource::Resource> res(resource::Manager::create_resource( filename ));
	if( !res.get() )
		THROW(GluckBadResource(__FUNCTION__,"No precache file", filename ));

	if( !xml::StreamResource( res.get() ).load( &xml::Root(this, "Scene") ) )
		THROW(GluckBadResource(__FUNCTION__,"Cannot load precache file", filename ));
}

node::Node * Precacher::load_scene(const filesystem::String & filename, node::Node * parent)
{
	node::Node * node = 0;

	std::string str_to_lower;
	const char * buff = filename.c_str();
	for( unsigned i = 0; i < filename.size(); ++i )
	{
		str_to_lower += tolower(buff[i]);
	}
	
	filesystem::String lowered_name = str_to_lower.c_str();

	if(nodemap[lowered_name] != 0)
		node = nodemap[lowered_name]->Clone();
	else
	{
		if(on_a_level)
			animate::Soul::Creator::disable_registration();

#ifdef _DEBUG
		logs::log() << "precaching: " << filename.c_str() << "\n";
#endif
		
		node = lib3d::node::Factory::create( &precache_root, node::Node::node_name, Atom( filename.c_str() ) );
		boost::scoped_ptr<resource::Resource> res(resource::Manager::create_resource( filename ));	
		if( !xml::StreamResource( res.get() ).load( &xml::Root( node, "Scene" ) ) )
			THROW(GluckBadResource(__FUNCTION__, "Failed to load model", filename ));

		if(on_a_level)
		{
#ifdef _DEBUG
			logs::log() << "on a level" << "\n";
#endif
			animate::Soul::Creator::enable_registration();
			models_on_level.push_back(lowered_name);
			nodemap[lowered_name] = node;
			node = node->Clone();
		}
	}

	parent->AddChild(node, true);
	return node;
}

void Precacher::start_new_level()
{
#ifdef _DEBUG
	logs::log() << "new level start" << "\n";
#endif
	
	on_a_level = true;
}

void Precacher::end_new_level()
{
#ifdef _DEBUG
	logs::log() << "new level end" << "\n";
#endif
	
	on_a_level = false;
	for(size_t i = 0; i < models_on_level.size(); ++i)
	{
#ifdef _DEBUG
		logs::log() << "uncaching: " << models_on_level[i].c_str() << "\n";
#endif

		delete nodemap[models_on_level[i]]; nodemap[models_on_level[i]] = 0;
	}
	models_on_level.clear();
}


Precacher::NodeLoader::NodeLoader(Precacher * pre)
:	pre(pre)
{}

bool Precacher::NodeLoader::on_attribute(xml::Provider * prov, const std::string & name, const std::string & value)
{
	if(name == "Name")
	{
		this->name = value;
		return true;
	}
	if(name == "Children")
	{
		scene_name = value.c_str();
		return true;
	}

	return xml::ObjectANSI::on_attribute(prov, name, value);
}

bool Precacher::NodeLoader::on_end_node(xml::Provider * prov)
{
	pre->create_precached_node(name, scene_name);
	return true;
}

void Precacher::create_precached_node(const std::string & name, const filesystem::String & scene_name)
{
	if(!precaching)
	{
		delete nodemap[scene_name]; nodemap[scene_name] = 0;
		return;
	}

	if(nodemap[scene_name] != 0)
		return;

	node::Node * node = lib3d::node::Factory::create( &precache_root, node::Node::node_name, Atom( name ) );
	boost::scoped_ptr<resource::Resource> res(resource::Manager::create_resource( scene_name ));	
	if( !xml::StreamResource( res.get() ).load( &xml::Root( node, "Scene" ) ) )
		THROW(GluckBadResource(__FUNCTION__, "Failed to load model", scene_name ));

	nodemap[scene_name] = node;
}

bool Precacher::on_node(xml::Provider * prov, const std::string & name)
{
	if(name != "Node")
	{
		if(precaching)
		{
#ifdef _DEBUG
			logs::log() << "precaching: " << name << "\n";
#endif
			node::Node * child = node::Factory::create( &precache_root, Atom( name ) );
			if( child )
				return prov->parse_object( child );

			return false;
		}
		else
		{
#ifdef _DEBUG
			logs::log() << "uncaching: " << name << "\n";
#endif
			node::Node * child = precache_root.FindNode(Atom(name), false);
			delete child; child = 0;
			return true;
		}
	}

	NodeLoader loader(this);
	return prov->parse_object(&loader);
}


} //namespace lib3d
} //namespace liba